#include <cuda_runtime.h>

template <typename T>
int test_validation(T* rhc, T* thc, size_t n) {
    for (size_t i = 0; i < n; i ++) {
        if (std::abs(rhc[i] - thc[i]) > 0.001) {
            std::cerr << "rhc[" << i << "]: " << rhc[i] << " vs. thc[" << i << "]: " << thc[i] << std::endl;
            return 1;
        }
    }
    return 0;
}

int capture_cuda_error() {
    cudaError_t error = cudaGetLastError();
    if (error != cudaSuccess) {
        std::cerr << cudaGetErrorName(error) << ": " << cudaGetErrorString(error) << std::endl;
        return 1;
    }
    return 0;
}

#define CUDA_CHECK(call)                                                        \
    do {                                                                        \
        cudaError_t status = (call);                                            \
        if (status != cudaSuccess) {                                            \
            fprintf(stderr, "CUDA Error: %s\n", cudaGetErrorString(status));    \
            exit(status);                                                       \
        }                                                                       \
    } while(0)

#define CEIL_DIV(M, N) (((M) + (N)-1) / (N))

#define CUDA_CHECK_STATUS()             \
    if (capture_cuda_error() != 0) {    \
        return 1;                       \
    }

#define VALUE_CHECK(RHC, THC, N, NAME)                                  \
    if (test_validation(RHC, THC, N) != 0) {                            \
        std::cerr << #NAME << ": validation test failed." << std::endl; \
        return 1;                                                       \
    }                                                                   \
    std::cout << #NAME << ": validation test success." << std::endl;
